﻿//-----------------------------------------------------------------------
// <copyright file="SilverlightFaultBehavior.cs" company="CodePlex">
//     Copyright (c) Doug Holland, CodePlex. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace CodePlex.Diagnostics.Services
{
    using System;
    using System.Net;

    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;

    /// <summary>
    /// Defines the fault behavior for Silverlight clients.
    /// </summary>
    public sealed class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
    {
        #region public override Type BehaviorType

        /// <summary>
        /// Gets the <see cref="T:System.Type"/> of behavior.
        /// </summary>
        /// <value>The <see cref="T:System.Type"/> of behavior.</value>
        public override Type BehaviorType
        {
            get
            {
                return typeof(SilverlightFaultBehavior);
            }
        }

        #endregion

        #region public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)

        /// <summary>
        /// Implement to pass data at runtime to bindings to support custom behavior.
        /// </summary>
        /// <param name="endpoint">The endpoint to modify.</param>
        /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        #endregion

        #region public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)

        /// <summary>
        /// Implements a modification or extension of the client across an endpoint.
        /// </summary>
        /// <param name="endpoint">The endpoint that is to be customized.</param>
        /// <param name="clientRuntime">The client runtime to be customized.</param>
        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        #endregion

        #region public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)

        /// <summary>
        /// Implements a modification or extension of the service across an endpoint.
        /// </summary>
        /// <param name="endpoint">The endpoint that exposes the contract.</param>
        /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            SilverlightFaultMessageInspector inspector = new SilverlightFaultMessageInspector();

            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
        }

        #endregion

        #region public void Validate(ServiceEndpoint endpoint)

        /// <summary>
        /// Implement to confirm that the endpoint meets some intended criteria.
        /// </summary>
        /// <param name="endpoint">The endpoint to validate.</param>
        public void Validate(ServiceEndpoint endpoint)
        {
        }

        #endregion

        #region protected override object CreateBehavior()

        /// <summary>
        /// Creates a behavior extension based on the current configuration settings.
        /// </summary>
        /// <returns>The behavior extension.</returns>
        protected override object CreateBehavior()
        {
            return new SilverlightFaultBehavior();
        }

        #endregion

        #region private class SilverlightFaultMesageInspector : IDispatchMessageInspector

        /// <summary>
        /// Provides support for altering the <see cref="T:System.Net.HttpStatusCode"/> for faults, specifically for Silverlight clients.
        /// </summary>
        private class SilverlightFaultMessageInspector : IDispatchMessageInspector
        {
            #region public SilverlightFaultMessageInspector()

            /// <summary>
            /// Initializes a new instance of the <see cref="T:SilverlightFaultMessageInspector"/> class.
            /// </summary>
            public SilverlightFaultMessageInspector()
            {
            }

            #endregion

            #region public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)

            /// <summary>
            /// Called after an inbound message has been received but before the message is dispatched to the intended operation.
            /// </summary>
            /// <param name="request">The request message.</param>
            /// <param name="channel">The incoming channel.</param>
            /// <param name="instanceContext">The current service instance.</param>
            /// <returns>
            /// The object used to correlate state. This object is passed back in the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.BeforeSendReply(System.ServiceModel.Channels.Message@,System.Object)"/> method.
            /// </returns>
            public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
            {
                // do nothing to the incoming message.
                return null;
            }

            #endregion

            #region public void BeforeSendReply(ref Message reply, object correlationState)

            /// <summary>
            /// Called after the operation has returned but before the reply message is sent.
            /// </summary>
            /// <param name="reply">The reply message. This value is null if the operation is one way.</param>
            /// <param name="correlationState">The correlation object returned from the <see cref="M:System.ServiceModel.Dispatcher.IDispatchMessageInspector.AfterReceiveRequest(System.ServiceModel.Channels.Message@,System.ServiceModel.IClientChannel,System.ServiceModel.InstanceContext)"/> method.</param>
            public void BeforeSendReply(ref Message reply, object correlationState)
            {
                if (reply.IsFault)
                {
                    HttpResponseMessageProperty property = new HttpResponseMessageProperty();

                    // change the Http status code to 200.
                    property.StatusCode = HttpStatusCode.OK;

                    reply.Properties[HttpResponseMessageProperty.Name] = property;
                }
            }

            #endregion
        }

        #endregion
    }
}
